﻿namespace Harness
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Reflection;

    /// <summary>
    /// The factory class by Flyweight pattern (GOF)
    /// </summary>
    /// <typeparam name="T">The type of a class</typeparam>
    internal class FlyweightFactory<T>
    {
        #region Constants

        /// <summary>
        /// Namespace of a class 
        /// </summary>
        private readonly string classNameSpace;

        /// <summary>
        /// The suffix of a class
        /// </summary>
        private readonly string classNameSuffix;

        /// <summary>
        /// The dictionary which pools instance
        /// </summary>
        private Dictionary<string, T> pool = new Dictionary<string, T>();

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the FlyweightFactory class.
        /// </summary>
        /// <param name="classNameSpace">Namespace of a class</param>
        /// <param name="classNameSuffix">The suffix of a class</param>
        public FlyweightFactory(string classNameSpace, string classNameSuffix)
        {
            if (string.IsNullOrWhiteSpace(classNameSpace))
            {
                throw new ArgumentNullException("namespace");
            }

            if (string.IsNullOrWhiteSpace(classNameSpace))
            {
                throw new ArgumentNullException("suffix");
            }

            this.classNameSpace = classNameSpace;
            this.classNameSuffix = classNameSuffix;

            Assembly asm = Assembly.GetExecutingAssembly();
            Type[] types = asm.GetTypes();

            foreach (Type type in types)
            {
                if (classNameSpace == type.Namespace)
                {
                    Debug.Print("namespace = {0}", type.FullName);

                    T instance = (T)System.Activator.CreateInstance(type);
                    this.pool.Add(type.Name.ToUpper(), instance);
                }
            }
        }

        #endregion

        #region Public Methods

        public bool Exists(string name)
        {
            if (string.IsNullOrWhiteSpace(name))
            {
                throw new ArgumentNullException("name");
            }

            string key = this.BuildKey(name);

            return this.pool.ContainsKey(key);
        }

        public T Get(string name)
        {
            if (string.IsNullOrWhiteSpace(name))
            {
                throw new ArgumentNullException("name");
            }

            string key = this.BuildKey(name);

            if (!this.pool.ContainsKey(key))
            {
                throw new KeyNotFoundException(name);
            }

            return this.pool[key];
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Make the key for the pool
        /// </summary>
        /// <param name="name">target name</param>
        /// <returns>pool key</returns>
        private string BuildKey(string name)
        {
            return string.Format(
                "{0}{1}",
                name,
                this.classNameSuffix).ToUpper();
        }

        #endregion
    }
}
